/**
 * 加法运算，避免数据相加小数点后产生多位数和计算精度损失。
 *
 * @param num1加数1 | num2加数2
 */
export const accAdd = (arg1, arg2) => {
  let r1 = 0,
    r2 = 0,
    m = 0,
    c = 0
  try {
    r1 = arg1.toString().split('.')[1].length
  } catch (e) {
    r1 = 0
  }
  try {
    r2 = arg2.toString().split('.')[1].length
  } catch (e) {
    r2 = 0
  }
  c = Math.abs(r1 - r2)
  m = Math.pow(10, Math.max(r1, r2))
  if (c > 0) {
    const cm = Math.pow(10, c)
    if (r1 > r2) {
      arg1 = Number(arg1.toString().replace('.', ''))
      arg2 = Number(arg2.toString().replace('.', '')) * cm
    } else {
      arg1 = Number(arg1.toString().replace('.', '')) * cm
      arg2 = Number(arg2.toString().replace('.', ''))
    }
  } else {
    arg1 = Number(arg1.toString().replace('.', ''))
    arg2 = Number(arg2.toString().replace('.', ''))
  }
  return (arg1 + arg2) / m
}

/**
 * 减法运算，避免数据相减小数点后产生多位数和计算精度损失。
 *
 * @param num1被减数  |  num2减数
 */
export const numSub = (num1: number | string, num2: number | string) => {
  num1 = Number(num1)
  num2 = Number(num2)
  let baseNum = 0,
    baseNum1 = 0,
    baseNum2 = 0
  let precision = 0
  try {
    baseNum1 = num1.toString().split('.')[1].length
  } catch (e) {
    baseNum1 = 0
  }
  try {
    baseNum2 = num2.toString().split('.')[1].length
  } catch (e) {
    baseNum2 = 0
  }
  baseNum = Math.pow(10, Math.max(baseNum1, baseNum2))
  precision = baseNum1 >= baseNum2 ? baseNum1 : baseNum2
  return Number(((num1 * baseNum - num2 * baseNum) / baseNum).toFixed(precision))
}

/**
 * 乘法运算，避免数据相乘小数点后产生多位数和计算精度损失。
 *
 * @param num1被乘数 | num2乘数
 */
// export const numMulti = (num1, num2) => {
//   let baseNum = 0
//   try {
//     baseNum += num1.toString().split('.')[1].length
//   } catch (e) {}
//   try {
//     baseNum += num2.toString().split('.')[1].length
//   } catch (e) {}
//   return
//   (Number(num1.toString().replace('.', '')) * Number(num2.toString().replace('.',
//     ''))) / Math.pow(10, baseNum)
// }
/**
 * 除法运算，避免数据相除小数点后产生多位数和计算精度损失。
 *
 * @param num1被除数 | num2除数
 */
// export const numDiv = (num1, num2) => {
//   var baseNum1 = 0, baseNum2 = 0
//   var baseNum3, baseNum4
//   try {
//     baseNum1 = num1.toString().split('.')[1].length
//   } catch (e) {
//     baseNum1 = 0
//   }
//   try {
//     baseNum2 = num2.toString().split('.')[1].length
//   } catch (e) {
//     baseNum2 = 0
//   }
//   with (Math) {
//     baseNum3 = Number(num1.toString().replace('.', ''))
//     baseNum4 = Number(num2.toString().replace('.', ''))
//     return (baseNum3 / baseNum4) * pow(10, baseNum2 - baseNum1)
//   }
// }

export const add = (num1: number,num2: number) => {
  num1 = Number(num1)
  num2 = Number(num2)
  let dec1: number, dec2: number
  try { dec1 = countDecimals(num1)+1 } catch (e) { dec1 = 0 }
  try { dec2 = countDecimals(num2)+1 } catch (e) { dec2 = 0 }
  const times = Math.pow(10, Math.max(dec1, dec2))
  const result = Number((mul(num1, times) - mul(num2, times)) / times)
  return getCorrectResult("sub", num1, num2, result)
}

export const mul = (num1: number, num2: number): number => {
  num1 = Number(num1)
  num2 = Number(num2)
  let times = 0
  const s1 = num1.toString()
  const s2 = num2.toString()
  try { times += countDecimals(s1); } catch (e) { }
  try { times += countDecimals(s2); } catch (e) { }
  const result = convertToInt(s1) * convertToInt(s2) / Math.pow(10, times)
  return getCorrectResult("mul", num1, num2, result)
}

/**
 * 计算小数位的长度
 * @param {*} num
 * @returns {number}
 */
export const countDecimals = (num: any): number => {
  let len = 0;
  try {
    num = Number(num)
    let str = num.toString().toUpperCase();
    if (str.split('E').length === 2) { // 科学记数法
      let isDecimal = false
      if (str.split('.').length === 2) {
        str = str.split('.')[1]
        if (parseInt(str.split('E')[0]) !== 0) {
          isDecimal = true
        }
      }
      const x = str.split('E');
      if (isDecimal) {
        len = x[0].length
      }
      len -= parseInt(x[1]);
    } else if (str.split('.').length === 2) { // 十进制
      if (parseInt(str.split('.')[1]) !== 0) {
        len = str.split('.')[1].length
      }
    }
  } catch(e) {
    throw e
  } finally {
    if (isNaN(len) || len < 0) {
      len = 0
    }
    return len
  }
}

/**
 * 将小数转成整数
 * @param {*} num
 * @returns {*}
 */
export const convertToInt = (num: any): number => {
  num = Number(num)
  let newNum = num
  const times = countDecimals(num)
  const temp_num = num.toString().toUpperCase()
  if (temp_num.split('E').length === 2) {
    newNum = Math.round(num * Math.pow(10, times))
  } else {
    newNum = Number(temp_num.replace(".", ""))
  }
  return newNum
}

/**
 * 确认我们的计算结果无误，以防万一
 * @param {string} type
 * @param {number} num1
 * @param {number} num2
 * @param {number} result
 * @returns {number}
 */
export const getCorrectResult = (type: 'add' | 'sub' | 'div' | 'mul', num1: number, num2: number, result: number): number => {
  let temp_result = 0
  switch (type) {
    case "add":
      temp_result = num1 + num2
      break
    case "sub":
      temp_result = num1 - num2
      break
    case "div":
      temp_result = num1 / num2
      break
    case "mul":
      temp_result = num1 * num2
      break
  }
  if (Math.abs(result - temp_result) > 1) {
    return temp_result
  }
  return result
}

/*
  函数：除法函数，用来得到精确的除法结果
	        说明：函数返回较为精确的除法结果。
	        参数：arg1：除数；arg2被除数；d要保留的小数位数（可以不传此参数，如果不传则不处理小数位数)
	        调用：Calc.Div(arg1,arg2)
	        返回值：arg1除于arg2的结果
	        */
export const div = (arg1: any, arg2: any) => {
  const r1 = arg1.toString()
  const r2 = arg2.toString()
  const d = arguments[2];
  const m = (r2.split(".")[1] ? r2.split(".")[1].length : 0) - (r1.split(".")[1] ? r1.split(".")[1].length : 0);
  const resultVal = Number(r1.replace(".", "")) / Number(r2.replace(".", ""))
    * Math.pow(10, m);
  return typeof d !== "number" ? Number(resultVal) : Number(resultVal.toFixed(parseInt(d)));
}
